package com.chenwc.file;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 写文件工具类
 *
 * @author chenwc
 * @date 2023/8/31 22:08
 */
public class WriteFile {

    private static final Logger log = LoggerFactory.getLogger(WriteFile.class);

    /**
     * 流交换操作，把流写入到文件
     *
     * @param ins     输入流
     * @param tarFile 待写入文件
     */
    private static void transferStream(InputStream ins, File tarFile) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 10);
        ReadableByteChannel rbcInst = Channels.newChannel(ins);
        //获取目标输出文件的fileChannel
        FileChannel fileChannel = null;
        FileOutputStream fos = null;
        log.info("写入文件路径: {}", tarFile.getAbsolutePath());
        try {
            if (!tarFile.exists()) {
                mkdirParents(tarFile);
                if (!tarFile.createNewFile()) {
                    log.info("文件不存在，创建文件失败！");
                    return;
                } else {
                    log.info("文件不存在，创建文件成功！");
                }
            }
            long start = System.currentTimeMillis();
            log.info("开始写入文件............");
            fos = new FileOutputStream(tarFile);
            fileChannel = fos.getChannel();
            while (-1 != (rbcInst.read(byteBuffer))) {
                byteBuffer.flip();
                fileChannel.write(byteBuffer);
                byteBuffer.clear();
            }
            long end = System.currentTimeMillis();
            log.info("写入文件成功............");
            log.info("写入文件耗时: {} ms", String.valueOf((end - start)));
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            try {
                if (null != fos) {
                    fos.close();
                }
                if (null != rbcInst) {
                    rbcInst.close();
                }
                if (null != fileChannel) {
                    fileChannel.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 以UTF-8字符编码格式写入文本内容到文件（字符流）
     *
     * @param content 待写入文本内容
     * @param path    文件路径+文件名
     */
    public static void writeFileByWriter(String content, String path) {
        writeFileByWriter(content, path, StandardCharsets.UTF_8, false);
    }

    /**
     * 以UTF-8字符编码格式写入文本内容到文件（字符流）
     *
     * @param content  待写入文本内容
     * @param path     文件路径+文件名
     * @param isAppend 是否把把数据追加写入文件
     */
    public static void writeFileByWriter(String content, String path, boolean isAppend) {
        writeFileByWriter(content, path, StandardCharsets.UTF_8, isAppend);
    }

    /**
     * 以指定字符编码格式写入文本内容到文件（字符流）
     *
     * @param content  待写入文本内容
     * @param path     文件路径+文件名
     * @param charset  待写入文件的字符编码
     * @param isAppend 是否把把数据追加写入文件
     */
    public static void writeFileByWriter(String content, String path, Charset charset, boolean isAppend) {
        Writer writer = null;
        File file = new File(path);
        log.info("写入文件路径: {}", file.getAbsolutePath());
        try {
            if (!file.exists()) {
                mkdirParents(file);
                if (!file.createNewFile()) {
                    log.info("文件不存在，创建文件失败！");
                    return;
                } else {
                    log.info("文件不存在，创建文件成功！");
                }
            }
            long start = System.currentTimeMillis();
            log.info("开始写入文件............");
            if (isAppend) {
                writer = new OutputStreamWriter(new FileOutputStream(file, true), charset);
            } else {
                writer = new OutputStreamWriter(new FileOutputStream(file, false), charset);
            }
            writer.write(content);
            long end = System.currentTimeMillis();
            log.info("写入文件成功............");
            log.info("写入文件耗时: {} ms", String.valueOf((end - start)));
        } catch (IOException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        } finally {
            try {
                if (null != writer) {
                    writer.close();
                }
            } catch (Exception e2) {
                // TODO: handle exception
                e2.printStackTrace();
            }
        }
    }

    /**
     * 通过 FileOutputStream 写入文件（字节流）
     *
     * @param bytes 待写入文件的二进制内容
     * @param path  文件路径+文件名
     */
    public static void writeFileByFileOutputStream(byte[] bytes, String path) {
        FileOutputStream fileOutputStream = null;
        File file = new File(path);
        log.info("写入文件路径: {}", file.getAbsolutePath());
        try {
            if (!file.exists()) {
                mkdirParents(file);
                if (!file.createNewFile()) {
                    log.info("文件不存在，创建文件失败！");
                    return;
                } else {
                    log.info("文件不存在，创建文件成功！");
                }
            }
            long start = System.currentTimeMillis();
            log.info("开始写入文件............");
            fileOutputStream = new FileOutputStream(file, false);
            fileOutputStream.write(bytes);
            long end = System.currentTimeMillis();
            log.info("写入文件成功............");
            log.info("写入文件耗时: {} ms", String.valueOf((end - start)));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != fileOutputStream) {
                    fileOutputStream.close();
                }
            } catch (Exception e2) {
                // TODO: handle exception
                e2.printStackTrace();
            }
        }
    }

    /**
     * 把 InputStream 流写入本地文件（字节流）
     *
     * @param inputStream InputStream 流
     * @param filePath    待写入本地文件全路径+文件名
     */
    public static void writeFileByInputStream(InputStream inputStream, String filePath) {
        File file = new File(filePath);
        FileOutputStream fos = null;
        log.info("写入文件路径: {}", file.getAbsolutePath());
        try {
            if (!file.exists()) {
                mkdirParents(file);
                if (!file.createNewFile()) {
                    log.info("文件不存在，创建文件失败！");
                    return;
                } else {
                    log.info("文件不存在，创建文件成功！");
                }
            }
            long start = System.currentTimeMillis();
            log.info("开始写入文件............");
            fos = new FileOutputStream(file);
            byte[] b = new byte[1024 * 10];
            while ((inputStream.read(b)) != -1) {
                // 写入数据
                fos.write(b);
            }
            long end = System.currentTimeMillis();
            log.info("写入文件成功............");
            log.info("写入文件耗时: {} ms", String.valueOf((end - start)));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != inputStream) {
                    inputStream.close();
                }
                if (null != fos) {
                    //保存数据
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 判断文件的父级目录是否存在，不存在则创建
     *
     * @param file 文件
     * @return true 父级目录存在或创建父级目录成功， false创建父级目录失败
     */
    private static boolean mkdirParents(File file) {
        if (!file.getParentFile().exists()) {
            return file.getParentFile().mkdirs();
        } else {
            return true;
        }
    }

}